package edu.gatech.cs3240;

import java.util.Hashtable;

/**
 * Represents a set of production rules, which dictate the pattern that the 
 * regex must follow.  The grammar provided for the CS3240 project has been 
 * hardcoded here, but it is fairly obvious to see that a different grammar
 * could just as easily be used.
 * @author VRamachandran
 */
public class Grammar {
	private Hashtable<String, Token> tok;
	private Token.Nonterminal startToken;
	
	public Grammar(){

		Token[] prod;
		
		/* these are all the non terminals in the grammar but 
		 * need to be changed to reflect the left factored grammar.
		 * as well as the modification from precidences
		 */
		
		tok = new Hashtable<String, Token>();
			
		tok.put("EXP",(new Token.Nonterminal("EXP")));
		tok.put("PROD",(new Token.Nonterminal("PROD")));
		tok.put("TERM",(new Token.Nonterminal("TERM")));
		tok.put("DOTTAIL",(new Token.Nonterminal("DOTTAIL")));
		tok.put("PIPETAIL",(new Token.Nonterminal("PIPETAIL")));
		tok.put("OP",(new Token.Nonterminal("OP")));
		tok.put("<prim>",(new Token.Nonterminal("<prim>")));
		tok.put("<L1-tail>",(new Token.Nonterminal("<L1-tail>")));
		tok.put("<L2-tail>",(new Token.Nonterminal("<L2-tail>")));
		tok.put("(",(new Token.Terminal('(')));
		tok.put(")",(new Token.Terminal(')')));
		tok.put("+",(new Token.Terminal('+')));
		tok.put("?",(new Token.Terminal('?')));
		tok.put("*",(new Token.Terminal('*')));
		tok.put("a",(new Token.Terminal('a')));
		tok.put("b",(new Token.Terminal('b')));
		tok.put("|",(new Token.Terminal('|')));
		tok.put(".",(new Token.Terminal('.')));
		tok.put("EPSILON", Token.Terminal.EPSILON);
		
			//sets the start state
		setStartToken((Token.Nonterminal) tok.get("EXP"));
		
		// EXP -> PROD PIPETAIL
		prod = new Token[] {
				tok.get("PROD"),
				tok.get("PIPETAIL")
		};
		((Token.Nonterminal) tok.get("EXP")).putProduction(prod);
		((Token.Nonterminal) tok.get("EXP")).setExpansion('(', prod);
		((Token.Nonterminal) tok.get("EXP")).setExpansion('a', prod);
		((Token.Nonterminal) tok.get("EXP")).setExpansion('b', prod);
		
		// PROD -> TERM DOTTAIL
		prod = new Token[] {
				tok.get("TERM"),
				tok.get("DOTTAIL")
		};
		((Token.Nonterminal) tok.get("PROD")).putProduction(prod);
		((Token.Nonterminal) tok.get("PROD")).setExpansion('(', prod);
		((Token.Nonterminal) tok.get("PROD")).setExpansion('a', prod);
		((Token.Nonterminal) tok.get("PROD")).setExpansion('b', prod);
		
		// TERM -> (EXP)
		prod = new Token[] {
				tok.get("("),
				tok.get("EXP"),
				tok.get(")")
		};
		((Token.Nonterminal) tok.get("TERM")).putProduction(prod);
		((Token.Nonterminal) tok.get("TERM")).setExpansion('(', prod);
		
		// TERM -> <prim> OP
		prod = new Token[] {
				tok.get("<prim>"),
				tok.get("OP")
		};
		((Token.Nonterminal) tok.get("TERM")).putProduction(prod);
		((Token.Nonterminal) tok.get("TERM")).setExpansion('a', prod);
		((Token.Nonterminal) tok.get("TERM")).setExpansion('b', prod);
		
		// PIPETAIL -> | PROD PIPETAIL
		prod = new Token[] {
				tok.get("|"),
				tok.get("PROD"),
				tok.get("PIPETAIL")
		};
		((Token.Nonterminal) tok.get("PIPETAIL")).putProduction(prod);
		((Token.Nonterminal) tok.get("PIPETAIL")).setExpansion('|', prod);
		
		// PIPETAIL -> NULL
		prod = new Token[] {
				Token.Terminal.EPSILON
		};
		((Token.Nonterminal) tok.get("PIPETAIL")).putProduction(prod);
		
		// DOTTAIL -> . TERM DOTTAIL
		prod = new Token[] {
				tok.get("."),
				tok.get("TERM"),
				tok.get("DOTTAIL")
		};
		((Token.Nonterminal) tok.get("DOTTAIL")).putProduction(prod);
		((Token.Nonterminal) tok.get("DOTTAIL")).setExpansion('.', prod);
		
		// DOTTAIL -> NULL
		prod = new Token[] {
				Token.Terminal.EPSILON
		};
		((Token.Nonterminal) tok.get("DOTTAIL")).putProduction(prod);
		
		// <prim> -> a
		prod = new Token[] {
				tok.get("a")
		};
		((Token.Nonterminal) tok.get("<prim>")).putProduction(prod);
		((Token.Nonterminal) tok.get("<prim>")).setExpansion('a', prod);
		
		// <prim> -> b
		prod = new Token[] {
				tok.get("b")
		};
		((Token.Nonterminal) tok.get("<prim>")).putProduction(prod);
		((Token.Nonterminal) tok.get("<prim>")).setExpansion('b', prod);
		
		// OP -> *
		prod = new Token[] {
				tok.get("*")
		};
		((Token.Nonterminal) tok.get("OP")).putProduction(prod);
		((Token.Nonterminal) tok.get("OP")).setExpansion('*', prod);
		
		// OP -> +
		prod = new Token[] {
				tok.get("+")
		};
		((Token.Nonterminal) tok.get("OP")).putProduction(prod);
		((Token.Nonterminal) tok.get("OP")).setExpansion('+', prod);
		
		// OP -> ?
		prod = new Token[] {
				tok.get("?")
		};
		((Token.Nonterminal) tok.get("OP")).putProduction(prod);
		((Token.Nonterminal) tok.get("OP")).setExpansion('?', prod);
		
		// OP -> NULL
		prod = new Token[] {
				Token.Terminal.EPSILON
		};
		((Token.Nonterminal) tok.get("OP")).putProduction(prod);
		
	}
	
	private void setStartToken(Token.Nonterminal startToken) {
		this.startToken = startToken;
	}

	public Token.Nonterminal getStartToken(){
		return startToken;
	}
	
}
